package cn.tlb.gl.face20_3d_light;

import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.MotionEvent;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import static javax.microedition.khronos.opengles.GL10.GL_COLOR_ARRAY;
import static javax.microedition.khronos.opengles.GL10.GL_COLOR_BUFFER_BIT;
import static javax.microedition.khronos.opengles.GL10.GL_DEPTH_BUFFER_BIT;
import static javax.microedition.khronos.opengles.GL10.GL_FIXED;
import static javax.microedition.khronos.opengles.GL10.GL_FLOAT;
import static javax.microedition.khronos.opengles.GL10.GL_TRIANGLE_STRIP;
import static javax.microedition.khronos.opengles.GL10.GL_UNSIGNED_BYTE;
import static javax.microedition.khronos.opengles.GL10.GL_VERTEX_ARRAY;

/**
 * 二十面体
 */
public class Face20_3D_Light_Activity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        GLSurfaceView mGlSurfaceView = new GLSurfaceView(this);
        mGlSurfaceView.setRenderer(new Face20Renderer());
        setContentView(mGlSurfaceView);
    }
    private float rotateX;
    private float rotateY;
    private float touchX;
    private float touchY;
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch(event.getAction()){
            case MotionEvent.ACTION_DOWN:
                touchX = event.getX();
                touchY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                rotateX += (event.getX()-touchX)/20;
                rotateY += ((event.getY()-touchY))/20;
                break;
            default:
                break;
        }
        return true;
    }

    private class Face20Renderer implements GLSurfaceView.Renderer{

        private float rot = 0.0f;

        private final float vertices[]= {
                0, -0.525731f, 0.850651f,             // vertices[0]
                0.850651f, 0, 0.525731f,              // vertices[1]
                0.850651f, 0, -0.525731f,             // vertices[2]
                -0.850651f, 0, -0.525731f,            // vertices[3]
                -0.850651f, 0, 0.525731f,             // vertices[4]
                -0.525731f, 0.850651f, 0,             // vertices[5]
                0.525731f, 0.850651f, 0,              // vertices[6]
                0.525731f, -0.850651f, 0,             // vertices[7]
                -0.525731f, -0.850651f, 0,            // vertices[8]
                0, -0.525731f, -0.850651f,            // vertices[9]
                0, 0.525731f, -0.850651f,             // vertices[10]
                0, 0.525731f, 0.850651f               // vertices[11]
        };

        private final int colors[] = {
                0xFFFF, 0, 0, 0xFFFF,
                0xFFFF, 0x7777, 0, 0xFFFF,
                0xFFFF, 0xFFFF, 0, 0xFFFF,
                0x7777, 0xFFFF, 0, 0xFFFF,
                0, 0xFFFF, 0, 0xFFFF,
                0, 0xFFFF, 0x7777, 0xFFFF,
                0, 0xFFFF, 0xFFFF, 0xFFFF,
                0, 0x7777, 0xFFFF, 0xFFFF,
                0, 0, 0xFFFF, 0xFFFF,
                0x7777, 0, 0xFFFF, 0xFFFF,
                0xFFFF, 0, 0xFFFF, 0xFFFF,
                0xFFFF, 0, 0x7777, 0xFFFF
        };

        private final byte indices[] = {
                1, 2, 6,
                1, 7, 2,
                3, 4, 5,
                4, 3, 8,
                6, 5, 11,
                5, 6, 10,
                9, 10, 2,
                10, 9, 3,
                7, 8, 9,
                8, 7, 0,
                11, 0, 1,
                0, 11, 4,
                6, 2, 10,
                1, 6, 11,
                3, 5, 10,
                5, 4, 11,
                2, 7, 9,
                7, 1, 0,
                3, 9, 8,
                4, 8, 0
        };
        // 各的顶点平均法向量
        float[] normals = {
                0.000000f, -0.417775f, 0.675974f,
                0.675973f, 0.000000f, 0.417775f,
                0.675973f, -0.000000f, -0.417775f,
                -0.675973f, 0.000000f, -0.417775f,
                -0.675973f, -0.000000f, 0.417775f,
                -0.417775f, 0.675974f, 0.000000f,
                0.417775f, 0.675973f, -0.000000f,
                0.417775f, -0.675974f, 0.000000f,
                -0.417775f, -0.675974f, 0.000000f,
                0.000000f, -0.417775f, -0.675973f,
                0.000000f, 0.417775f, -0.675974f,
                0.000000f, 0.417775f, 0.675973f,
        };
        private FloatBuffer verticesBuffer = array2FloatBuffer(vertices);
        private IntBuffer colorsBuffer = array2IntBuffer(colors);
        private ByteBuffer icosahedronFacesBuffer = ByteBuffer.wrap(indices);

        @Override
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
            gl.glDisable(GL10.GL_DITHER);
            gl.glShadeModel(GL10.GL_SMOOTH);

            gl.glClearColor(0, 0, 0, 0);
            gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
            gl.glEnable(GL10.GL_DEPTH_TEST);
            gl.glDepthFunc(GL10.GL_LEQUAL);

            // 在 OpenGL ES中，光由三个元素组成，分别是
            // 环境元素（ambient component）， 散射元素（diffuse component）和 高光元素（specular component）。
            //启用光源
            gl.glEnable(GL10.GL_LIGHTING);
            // 开启第一盏灯   GL_LIGHT0 ~ GL_LIGHT7
            gl.glEnable(GL10.GL_LIGHT0);
            //配置环境光 微弱白色光源
            FloatBuffer lightBuffer = array2FloatBuffer(new float[]{0.05f, 0.05f, 0.05f, 1.0f});
            gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, lightBuffer); //设置环境光
            //设置散射光
            gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, array2FloatBuffer(new float[]{0.5f, 0.5f, 0.5f, 1.0f}));
            //设置高光
            gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_SPECULAR, array2FloatBuffer(new float[]{10.7f, 10.7f, 10.7f, 1.0f}));
            //设置光照位置    此位置将第一个光源放置在原点向左5个单位
            gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, array2FloatBuffer(new float[]{-5.0f, 0, 0, 0.0f}));
            // 设置方向光    方向(1, 0, 0)
            gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_SPOT_DIRECTION, array2FloatBuffer(new float[]{1, 0, 0}));
            // 设置方向光范围  45°x2
            gl.glLightf(GL10.GL_LIGHT0, GL10.GL_SPOT_CUTOFF, 45.0f);
            // TODO 方向光设置完成如果没有看到光效, 可能是没有光照射到, 调整方向光范围或者光源位置
            // 至此设置光效是一个灰度的图像, 需要启用GL_COLOR_MATERIAL
            //gl.glEnable(GL10.GL_COLOR_MATERIAL);
        }

        @Override
        public void onSurfaceChanged(GL10 gl, int width, int height) {
            gl.glViewport(0, 0, width, height);
            gl.glMatrixMode(GL10.GL_PROJECTION);
            gl.glLoadIdentity();
            float ratio = (float)width/height;
            gl.glFrustumf(-2, 2, -2, 2, 1, 10);
        }

        @Override
        public void onDrawFrame(GL10 gl) {
            gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
            gl.glMatrixMode(GL10.GL_MODELVIEW);
            gl.glEnableClientState(GL_VERTEX_ARRAY);
            gl.glEnableClientState(GL_COLOR_ARRAY);
            //启用法线数组
            gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
            gl.glLoadIdentity();

            gl.glTranslatef(0.0f, 0.0f, -2.0f);
            gl.glRotatef(rotateX, 0, .2f, 0);
            gl.glRotatef(-rotateY, 0.2f, 0, 0);
            gl.glVertexPointer(3, GL_FLOAT, 0, verticesBuffer);
            gl.glColorPointer(4, GL_FIXED, 0, colorsBuffer);
            //传递法线数组
            gl.glNormalPointer(GL_FLOAT, 0, array2FloatBuffer(normals));
            gl.glDrawElements(GL_TRIANGLE_STRIP, icosahedronFacesBuffer.remaining(), GL_UNSIGNED_BYTE, icosahedronFacesBuffer);

            gl.glFinish();
            gl.glDisableClientState(GL_VERTEX_ARRAY);
            gl.glDisableClientState(GL_COLOR_ARRAY);
            gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
        }

        private FloatBuffer array2FloatBuffer(float fs[]){
            ByteBuffer byteBuffer = ByteBuffer.allocateDirect(fs.length * 4);
            byteBuffer.order(ByteOrder.nativeOrder());
            FloatBuffer floatBuffer = byteBuffer.asFloatBuffer();
            floatBuffer.put(fs);
            floatBuffer.position(0);
            return floatBuffer;
        }

        private IntBuffer array2IntBuffer(int ints[]){
            ByteBuffer byteBuffer = ByteBuffer.allocateDirect(ints.length*4);
            byteBuffer.order(ByteOrder.nativeOrder());
            IntBuffer intBuffer = byteBuffer.asIntBuffer();
            intBuffer.put(ints);
            intBuffer.position(0);
            return intBuffer;
        }
    }
}
